# Copyright (c) 2012 MaxMedia and Travis Warlick
# Licensed under the MIT License (see LICENSE)

class Module
  # Defines a singleton-class attribute writer that ensures the value is always converted to the
  # given type by using the method in the options or creating a new instance of the `type` class
  # and passing the value to its initializer.
  #
  # @overload class_attr_writer_typed(type, *names)
  #   @param type [Class] class to ensure the value is
  #   @param names [*Symbol] names of attributes
  #
  # @overload class_attr_writer_typed(type, *names, opts={})
  #   @param type [Class] class to ensure the value is
  #   @param names [*Symbol] names of attributes
  #   @param opts [Hash] options
  #   @option opts [Symbol] :method method name to use to convert the values
  def class_attr_writer_typed(type, *names)
    options = names.extract_options!

    names.each do |name|
      class_eval <<-EOC, __FILE__, __LINE__+1
        def self.#{name}=(val)
          obj = #{options[:method] ? "val.#{options[:method]}" : "val"}
          @#{name} = (obj.is_a?(#{type}) ? obj : #{type}.new(obj))
        end
      EOC
    end
  end

  # Defines a singleton-class attribute reader that has ensured the value has been converted to the
  # given type.
  #
  # @param type [Class] class to ensure the value is
  # @param names [*Symbol] names of attributes
  # @return [Object] value will be of type defined in the parameters
  # @raise [TypeError] if the value has been modified and is no longer of the proper type
  def class_attr_reader_typed(type, *names)
    options = names.extract_options!

    names.each do |name|
      class_eval <<-EOC, __FILE__, __LINE__+1
        def self.#{name}
          unless @#{name}.is_a?(#{type})
            raise TypeError, "Expected #{name} to be a #{type}"
          end
          @#{name}
        end
      EOC
    end
  end

  # Defines a singleton-class attribute reader and writer that ensures the value is always
  # converted to the given type.
  #
  # @see #class_attr_writer_typed
  # @see #class_attr_reader_typed
  def class_attr_accessor_typed(type, *names)
    options = names.extract_options!

    class_attr_reader_typed type, *names
    class_attr_writer_typed type, *names
  end
end
